ニューラルネットワーク(Neural Network, NN)
Overview
ニューラルネットワークは、近年「ディープラーニング」という名前で再度注目を集めているアルゴリズムです。ニューラルネットワークでは、下図のように、隠れ層という処理ステップを持ちますが、一般に、この隠れ層が2層以上持つものをディープニューラルネットワーク(Deep Neural Network)といいます。
また、隠れ層を1つも持たない、入力層と出力層のものを単純パーセプトロン(Simple Perceptron)と呼び、1つ以上隠れ層を持つものを多層パーセプトロン(Multilayer Perceptron, MLP)と呼びます。ここでは、MLPについてまとめます。
https://gyazo.com/0555d23325a5e8d5375b6e1e61ea25d9
Theory
上図で、inputsは入力特徴量を表し、接続している線が学習された係数を、outputが出力を表します。出力は、入力に対する重み付き和になっています。MLPでは、この重み付き和の計算が繰り返し行われます。まず、中間処理ステップを表す各れユニット(hidden units)の計算で重み付き和が行われ、次に、この隠れユニットの値に対して重み付き和が行われて、最後の結果が算出されます。
一連の重み付き和を計算することは、数学的には1つの重み付き和を計算することと同じなので、このモデルを線形モデルよりも強力にするためには、もう少し工夫する必要があります。そこで、個々の隠れユニットの重み付き和を計算したら、その結果に対して非線形活性化関数を適用します。多くの場合、relu(rectified linear unit, 正規化線形関数)やtanh(hyperbolic tangent, 双曲正接関数)が用いられます。
relu関数とtanh関数
https://gyazo.com/dde6924d7f47e0b9ddb175d5b3819e7a
Coding(Classification)
two_moonsデータセットでモデルを構築・学習・決定境界を可視化する
code: Python
import matplotlib.pyplot as plt
import mglearn
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
mlp = MLPClassifier(solver='lbfgs', random_state=0).fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train) plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.show()
https://gyazo.com/d790193d89f927697552b5d3104a15e5
ニューラルネットワークは、非線形ですが、比較的滑らかな決定境界を学習しています。デフォルトでは、MLPは100隠れユニットを用います。これはこの小さなデータセットに対しては大きすぎるので、この数を減らし、モデルの複雑さを減らしても良い結果が得られます。
two_moonsデータセットで隠れユニット数を減らしてモデルを構築・学習・決定境界を可視化する
code: Python
import matplotlib.pyplot as plt
import mglearn
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
mlp = MLPClassifier(solver='lbfgs', random_state=0, hidden_layer_sizes=10).fit(X_train, y_train) mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train) plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.show()
https://gyazo.com/8ab2f3cf01dfb68efd8dad8185b04ad6
隠れユニットを10にすると、決定境界は少しギザギザになります。デフォルトは、非線形化にreluを用います。決定境界をもう少し滑らかにしたければ、隠れ層のユニット数を増やすか、隠れ層を増やすか、非線形活性化関数にtanhを用いればよいです。
two_moonsデータセットで隠れ層を2層使ってモデルを構築・学習・決定境界を可視化する
code: Python
import matplotlib.pyplot as plt
import mglearn
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
# 隠れ層を2層使う
mlp = MLPClassifier(solver='lbfgs', random_state=0, hidden_layer_sizes=10, 10).fit(X_train, y_train) mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train) plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.show()
https://gyazo.com/6536a7a3ac7564d66346ed9aa326b59c
two_moonsデータセットで隠れ層を2層使い非線形活性化関数にtanhを使ってモデルを構築・学習・決定境界を可視化する
code: Python
import matplotlib.pyplot as plt
import mglearn
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
# 隠れ層を2層使う
# 非線形活性化関数にtanhを使う
mlp = MLPClassifier(solver='lbfgs', activation='tanh', random_state=0, hidden_layer_sizes=10, 10).fit(X_train, y_train) mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train) plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.show()
https://gyazo.com/8bf19efc9c76546487ed3835179dfd9d
また、ニューラルネットワークで正則化をするには、alphaパラメータを調整します。デフォルトでは、非常に小さな値になっていて、ほとんど正則化していません。
パラメータalphaの効果を可視化して比較する
code: Python
fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for axx, n_hidden_nodes in zip(axes, 10, 100): mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train ,ax=ax) ax.set_title("n_hidden={}\nalpha={:.4f}".format(n_hidden_nodes, n_hidden_nodes, alpha))
plt.show()
https://gyazo.com/ec2876f8970a4414052f2dd5ab1c7351
また、ニューラルネットワークでは、学習を開始する前に重みの初期値を乱数で割り当てます。これは学習されるモデルに影響を与えます。
パラメータalphaの効果を可視化して比較する
code: Python
fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for i, ax in enumerate(axes.ravel()):
mlp = MLPClassifier(solver='lbfgs', random_state=i, hidden_layer_sizes=100, 100) mlp.fit(X_train, y_train)
mglearn.plots.plot_2d_separator(mlp, X_train, fill=True, alpha=.3, ax=ax)
mglearn.discrete_scatter(X_train:, 0, X_train:, 1, y_train, ax=ax) plt.show()
https://gyazo.com/46aaafec025220c52eec77ccbc26cc6a
Advanced
ニューラルネットワークのためのデータ前処理
特徴量の多いcancerデータセットに対してMLPを適用してみます。
code: Python
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
print('Cancer data per-feature maximum:\n{}'.format(cancer.data.max(axis=0)))
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)
mlp = MLPClassifier(random_state=42).fit(X_train, y_train)
print("Accuracy on training set: {:.2f}".format(mlp.score(X_train, y_train)))
print("Accuracy on test set: {:.2f}".format(mlp.score(X_test, y_test)))
--------------------------------------------------------------------------
Cancer data per-feature maximum:
[2.811e+01 3.928e+01 1.885e+02 2.501e+03 1.634e-01 3.454e-01 4.268e-01
2.012e-01 3.040e-01 9.744e-02 2.873e+00 4.885e+00 2.198e+01 5.422e+02
3.113e-02 1.354e-01 3.960e-01 5.279e-02 7.895e-02 2.984e-02 3.604e+01
4.954e+01 2.512e+02 4.254e+03 2.226e-01 1.058e+00 1.252e+00 2.910e-01
6.638e-01 2.075e-01]
Accuracy on training set: 0.91
Accuracy on test set: 0.88
--------------------------------------------------------------------------
精度は悪くないですが、その他モデルと比較するといまいちです。データのスケールをそろえるとさらに良くなります。理想的には、平均が0で分散が1であるのが望ましいです。
code: Python
# 訓練データセットの特徴量ごとの平均値を算出
mean_on_train = X_train.mean(axis=0)
# 訓練データセットの特徴量ごとの標準偏差を算出
std_on_train = X_train.std(axis=0)
# 平均を引き、標準偏差の逆数でスケール変換する
X_train_scaled = (X_train - mean_on_train) / std_on_train
X_test_scaled = (X_test - mean_on_train) / std_on_train
# MLPを適用
mlp = MLPClassifier(max_iter=1000, random_state=0).fit(X_train_scaled, y_train)
print("Accuracy on training set: {:.3f}".format(mlp.score(X_train_scaled, y_train)))
print("Accuracy on test set: {:.3f}".format(mlp.score(X_test_scaled, y_test)))
--------------------------------------------------------------------------
Accuracy on training set: 0.993
Accuracy on test set: 0.972
--------------------------------------------------------------------------
このように、スケール変換を行うと、結果がはるかに良くなります。
Summary
Merit
信じられないほど複雑なモデルを構築できる
Demerit
学習に時間がかかる
データを慎重に前処理する必要がある
Parameters
hidden_layer_sizes
隠れ層の数と隠れユニット数を指定する
隠れ層は1つか2つで始め、あとから拡張していけばよい
隠れ層あたりのノードの数は入力層と同じくらいにすることが多いが、数千より大きくなることはあまりない
alpha
正則化パラメータ
増やすと正則化が強くなる
solver
パラメータを学習する際に用いるアルゴリズムを指定する
デフォルトはadamでほとんどのケースでよく機能するが、データのスケールにとても敏感 → したがって、データを平均0、分散1にしておくことが重要になる
lbfgsは、頑健だがモデルが大きい場合や、大規模なデータセットに対しては、学習に時間がかかる
sgdは、ディープラーニングでよく使われる